using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Specialized;

namespace Sio.Mdm.Utils
{
    /// <summary>
    /// Helper class for parsing command line. All formats are supported.
    /// </summary>
    /// <example>
    /// This is command line example
    /// -size=100 /height:'400' -param1 "Nice stuff !" --debug
    /// </example>
    public sealed class CommandLineHelper
    {
        #region Construction
        /// <summary>
        /// COnstruct command line helper
        /// </summary>
        public CommandLineHelper()
            : this(Environment.GetCommandLineArgs())
        {
        }
        /// <summary>
        /// Construct command line parsers helper
        /// </summary>
        /// <param name="args">Application arguments</param>
        public CommandLineHelper(string[] args)
        {
            Regex Spliter = new Regex(@"^-{1,2}|^/|=|:", RegexOptions.IgnoreCase | RegexOptions.Compiled);
            Regex Remover = new Regex(@"^['""]?(.*?)['""]?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

            string parameter = null;
            string[] parts;

            // Valid _parameters forms:
            // {-,/,--}param{ ,=,:}((",')value(",'))
            // Examples: 
            // -param1 value1 --param2 /param3:"Test-:-work" 
            //   /param4=happy -param5 '--=nice=--'
            foreach (string text in args)
            {
                // Look for new _parameters (-,/ or --) and a
                // possible enclosed value (=,:)
                parts = Spliter.Split(text, 3);

                switch (parts.Length)
                {
                    // Found a value (for the last parameter 
                    // found (space separator))
                    case 1:
                        if (parameter != null)
                        {
                            if (!_parameters.ContainsKey(parameter))
                            {
                                parts[0] =
                                    Remover.Replace(parts[0], "$1");

                                _parameters.Add(parameter.ToLower(), parts[0]);
                            }
                            parameter = null;
                        }
                        // else Error: no parameter waiting for a value (skipped)
                        break;

                    // Found just a parameter
                    case 2:
                        // The last parameter is still waiting. 
                        // With no value, set it to true.
                        if (parameter != null)
                        {
                            if (!_parameters.ContainsKey(parameter))
                                _parameters.Add(parameter.ToLower(), null);
                        }
                        parameter = parts[1];
                        break;

                    // Parameter with enclosed value
                    case 3:
                        // The last parameter is still waiting. 
                        // With no value, set it to true.
                        if (parameter != null)
                        {
                            if (!_parameters.ContainsKey(parameter))
                                _parameters.Add(parameter.ToLower(), null);
                        }

                        parameter = parts[1];

                        // Remove possible enclosing characters (",')
                        if (!_parameters.ContainsKey(parameter))
                        {
                            parts[2] = Remover.Replace(parts[2], "$1");
                            _parameters.Add(parameter.ToLower(), parts[2]);
                        }

                        parameter = null;
                        break;
                }
            }
            // In case a parameter is still waiting
            if (parameter != null)
            {
                if (!_parameters.ContainsKey(parameter))
                    _parameters.Add(parameter.ToLower(), "true");
            }

        }
        #endregion

        #region Properties

        #region Indexer
        private StringDictionary _parameters = new StringDictionary();
        /// <summary>
        /// Indexer for parameters parsed
        /// </summary>
        /// <param name="parameterName">Name of command line parameter to retreive</param>
        /// <returns>Value of specified parameter or null if parameter not in command line</returns>
        public string this[string parameterName]
        {
            get
            {
                if (!_parameters.ContainsKey(parameterName.ToLower()))
                    return null;

                return _parameters[parameterName];
            }
        }
        #endregion

        #endregion

        #region Methods

        #region Public

        #region HasParameter
        /// <summary>
        /// Test wheater specified parameter was passed for command line
        /// </summary>
        /// <param name="parameterName">Name of parameter to test for</param>
        /// <returns>True if parameter was passed to application</returns>
        public bool HasParameter(string parameterName)
        {
            return _parameters.ContainsKey(parameterName.ToLower());
        }
        #endregion

        #region Dispose
        /// <summary>
        /// Dispose command line parameters
        /// </summary>
        public void Dispose()
        {
            if (_parameters != null)
            {
                _parameters.Clear();
                _parameters = null;
            }
        }
        #endregion

        #region Typed getters

        #region GetParameter
        /// <summary>
        /// Get parameters for specified command
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public string GetParameter(string parameterName)
        {
            return this[parameterName];
        }
        #endregion

        #region GetParameterAsInteger
        /// <summary>
        /// Get parameter as integer
        /// </summary>
        /// <param name="parameterName">Command line parameter to get as integer</param>
        /// <returns>Parsed integer number or <see cref="int.MinValue"/> if value can not be parsed</returns>
        public int GetParameterAsInteger(string parameterName)
        {
            if (!HasParameter(parameterName))
                return int.MinValue;

            int value = int.MinValue;

            int.TryParse(_parameters[parameterName], out value);

            return value;
        }
        #endregion

        #region GetParameterAsDouble
        /// <summary>
        /// Get parameter as double
        /// </summary>
        /// <param name="parameterName">Command line parameter to get as integer</param>
        /// <returns>Parsed integer number or <see cref="Double.NaN"/> if value can not be parsed</returns>
        public double GetParameterAsDouble(string parameterName)
        {
            if (!HasParameter(parameterName))
                return Double.NaN;

            double value = double.MinValue;

            double.TryParse(_parameters[parameterName], out value);

            return value;
        }
        #endregion

        #region GetParameterAsDate
        /// <summary>
        /// Get parameter parsed as <see cref="DateTime"/>
        /// </summary>
        /// <param name="parameterName">Command line parameter to get as integer</param>
        /// <returns>Parsed integer number or <see cref="DateTime.MinValue"/> if value can not be parsed</returns>
        public DateTime GetParameterAsDate(string parameterName)
        {
            if (!HasParameter(parameterName))
                return DateTime.MinValue;

            DateTime value = DateTime.MinValue;

            DateTime.TryParse(_parameters[parameterName], out value);

            return value;
        }
        #endregion

        #region GetParameterAsGuid
        /// <summary>
        /// Get parameter parsed to <see cref="Guid"/>
        /// </summary>
        /// <param name="parameterName">Command line parameter to get as integer</param>
        /// <returns>Parsed integer number or <see cref="Guid.Empty"/> if value can not be parsed</returns>
        public Guid GetParameterAsGuid(string parameterName)
        {
            if (!HasParameter(parameterName))
                return Guid.Empty;

            Guid value = Guid.Empty;

            try
            {
                value = new Guid(_parameters[parameterName]);
            }
            catch
            {
            }

            return value;
        }
        #endregion

        #endregion

        #region FromCommandLine
        /// <summary>
        /// Get instance of parser from arguments passed from command line
        /// </summary>
        /// <param name="args">
        /// Passed argument. additional parameters can be added in application.
        /// Nice for controling command line driven application.
        /// </param>
        /// <returns>Instance of parsed command line arguments and values.</returns>
        /// <seealso cref="GetParameter"/>
        public static CommandLineHelper FromCommandLine(params string[] args)
        {
            return new CommandLineHelper(args);
        }
        #endregion

        #endregion

        #endregion
    }
}
